home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / program / recio215.zip / TUTOR.TXT < prev    next >
Text File  |  1996-10-26  |  18KB  |  474 lines

  1.     Title: A TUTORIAL INTRODUCTION TO THE C LANGUAGE RECIO LIBRARY
  2. Copyright: (C) 1994-1996, William Pierpoint
  3.   Version: 2.15
  4.      Date: October 26, 1996
  5.  
  6.  
  7.  
  8. 1.0 STDIO AND RECIO
  9.  
  10. The program many people first learned when introduced to the C programming
  11. language was the "hello, world" program published in Kernighan and Richie's
  12. "The C Programming Language."  And the first line of that first program,
  13.  
  14. #include <stdio.h>
  15.  
  16. tells the compiler that the functions and macros provided by the standard
  17. input/output library are needed for the program.  The "hello, world" program
  18. uses the powerful printf statement for output.  The counterpart for input,
  19. scanf, looks deceptively like printf, but unfortunately has many ways to
  20. trap an unwary programmer.  Common mistakes include failure to provide the 
  21. address of an variable, size of argument mismatched with the specification 
  22. in the format statement, and number of arguments mismatched with the 
  23. specification in the format statement.
  24.  
  25. Suppose you use a library that defines a boolean type as an unsigned
  26. character, 
  27.  
  28.     typedef unsigned char boolean;
  29.  
  30. You develop an output module that writes variables of type
  31. boolean to a file,
  32.  
  33.     /* output */
  34.     boolean state=0;
  35.     fprintf(fp, "%d", state);
  36.  
  37. where fp is a pointer to FILE.  Once you get the output module working, you
  38. decide to develop the input module to read back into the program the data
  39. you wrote to disk.
  40.  
  41.     /* input */
  42.     boolean state;
  43.     fscanf(fp, "%d", &state);
  44.  
  45. Is this ok?  On one compiler this worked consistently without problems,
  46. but on another compiler, it overwrote the value in another variable.  Why?
  47. Because fscanf is expecting the address of an integer, not an unsigned char.
  48. One compiler overwrote the adjoining memory address and the other compiler
  49. apparently did not.  Since compilers don't type check functions with 
  50. variable number of arguments, you don't get any errors or warnings.  That
  51. is what is so infuriating about this type of error.  You see that another
  52. variable has the wrong value, you check all the code that uses the other
  53. variable, and you can't find anything wrong with it.  In the midst of
  54. development, it is hard to imagine that the problem is caused by code that
  55. has nothing to do with the variable containing the bad value.
  56.  
  57. P.J. Plauger, in writing about the scanf functions in the book "The Standard 
  58. C Library" spends several pages discussing the dangers, limitations, and 
  59. ambiguities of scanf.  He concludes the section by saying,
  60.  
  61. "Be prepared, however, to give up on the scan functions beyond a point.
  62. Their usefulness, over the years, has proved to be limited."
  63.  
  64. The recio (record input/output) library takes a different approach to input.
  65. To input the boolean variable using the recio library, just write
  66.  
  67.     /* input */
  68.     boolean state;
  69.     state = rgeti(rp);
  70.  
  71. where rp is a pointer to REC (the recio structure analogous to the stdio 
  72. FILE structure).  The rgeti function gets the integer value from the input 
  73. and the compiler converts it to the boolean type when it makes the assignment.  
  74. No need to worry about crazy pointers here!
  75.  
  76. Since virtually every program has to do input or output, the stdio library
  77. is very familiar to C programmers.  Many functions in the recio library
  78. are analogous to the stdio library.  This makes the learning curve easier.
  79.  
  80.         Analogous stdio/recio components
  81.  
  82.     stdio        recio
  83.     ---------    ---------
  84.     FILE        REC
  85.     FOPEN_MAX    ROPEN_MAX
  86.  
  87.     stdin        recin
  88.     stdout        recout
  89.     stderr        recerr
  90.     stdprn        recprn
  91.  
  92.     fopen        ropen
  93.     fclose        rclose
  94.     fgets        rgetrec
  95.     fscanf        rgeti, rgetd, rgets, ...
  96.     fprintf         rputi, rputd, rputs, ...
  97.     clearerr    rclearerr
  98.     fgetpos        rgetfldpos
  99.     fsetpos        rsetfldpos
  100.     feof        reof
  101.     ferror        rerror
  102.  
  103. In addition, the recio library contains many new functions that don't 
  104. have a counterpart in stdio.  For example, you can easily read and 
  105. write dates and times using recio.
  106.   
  107.  
  108. 2.0 EXAMPLES
  109.  
  110. 2.1 Line Input
  111.  
  112. One of the first things you can do with the recio library to is to substitute
  113. rgetrec() for fgets() to get a line of text (record) from a file (or standard
  114. input).  The advantage of rgetrec() is that you don't have to go to the
  115. trouble to allocate space for a string buffer, or worry about the size of the
  116. string buffer.  The recio library handles that for you automatically.  The
  117. rgetrec function is like fgets() in that it gets a string from a stream, but
  118. it is like gets() in that it trims off the trailing newline character.
  119.  
  120. The echo program demonstrates the use of the rgetrec function.
  121.  
  122. /* echo.c - echo input to output */
  123.  
  124. #include <stdio.h>
  125. #include <stdlib.h>
  126.  
  127. #include "recio.h"
  128.  
  129. int main()
  130. {
  131.     /* while input continues to be available */
  132.     while (rgetrec(recin)) {
  133.  
  134.         /* echo record buffer to output */
  135.         puts(rrecs(recin));
  136.     }
  137.  
  138.     /* if exited loop before end-of-file */
  139.     if (!reof(recin)) {
  140.         exit (EXIT_FAILURE);
  141.     }
  142.     return (EXIT_SUCCESS);
  143. }
  144.  
  145. The echo program reads standard input using recin, the recio equivalent to
  146. stdin.  For output the recio library provides recout, recerr, and recprn.
  147.  
  148. The rgetrec function returns a pointer to the record buffer, but the echo
  149. program did not use a variable to hold a pointer to the string (although
  150. it could have).  Instead, the record buffer was accessed through the rrecs
  151. macro, which provides a pointer to the record buffer.
  152.  
  153. Since rgetrec returns NULL on either error or end-of-file, your program
  154. needs to find out which condition occurred.  You can use either the reof
  155. function or the rerror function to determine this.  The echo program uses
  156. the reof function; the wc program in section 2.2 uses the rerror function.
  157. The echo program just exits with a failure status if an error occurred
  158. before the end of the file was reached.
  159.  
  160.  
  161. 2.2 Line, Word, and Character Counting
  162.  
  163. The power of the recio library comes from its facilities to break records
  164. into fields and from the many functions that operate on fields.  Because
  165. the default field delimiter is the space character (which breaks on any
  166. whitespace), the default behavior is equivalent to subdividing a line of
  167. text into words.
  168.  
  169. The wc program counts lines, words, and characters for files specified
  170. on the command line.
  171.  
  172. /* wc.c - count lines, words, characters */
  173.  
  174. #include <stdio.h>
  175. #include <stdlib.h>
  176. #include <string.h>
  177.  
  178. #include "recio.h"
  179.  
  180. int main(int argc, char *argv[])
  181. {
  182.     int  nf;    /* number of files */
  183.     REC *rp;    /* pointer to open record stream */
  184.     long nc,    /* number of characters (not including line terminator) */
  185.          nw,    /* number of words */
  186.          nl;    /* number of lines */
  187.  
  188.     /* loop through all files */
  189.     for (nf=1; nf < argc; nf++) {
  190.  
  191.         /* open record stream */
  192.         rp = ropen(argv[nf], "r");
  193.         if (!rp) {
  194.             if (errno == ENOENT) {
  195.                 printf("ERROR: Could not open %s\n", argv[nf]);
  196.                 continue;
  197.             } else {
  198.                 printf("FATAL ERROR: %s\n", strerror(errno));
  199.                 exit (EXIT_FAILURE);
  200.             }
  201.         }
  202.  
  203.         /* initialize */
  204.         nc = nw = 0;
  205.         rsetfldch(rp, ' ');
  206.         rsettxtch(rp, ' ');
  207.  
  208.         /* loop through all lines (records) */
  209.         while (rgetrec(rp)) {
  210.  
  211.             /* count number of characters in line w/o '\n' */
  212.             nc += strlen(rrecs(rp));
  213.  
  214.             /* count number of words (fields) */
  215.             nw += rnumfld(rp);
  216.         }
  217.  
  218.         /* if exited loop on error rather than end-of-file */
  219.         if (rerror(rp)) {
  220.             printf("ERROR reading %s - %s\n", 
  221.              rnames(rp), rerrstr(rp));
  222.             exit (EXIT_FAILURE);
  223.         }
  224.  
  225.         /* get number of lines (records) */
  226.         nl = rrecno(rp);
  227.  
  228.         /* output results */
  229.         printf("%s: %ld %ld %ld\n", rnames(rp), nl, nw, nc);
  230.  
  231.         /* close record stream */
  232.         rclose(rp);
  233.     }
  234.     return (EXIT_SUCCESS);
  235. }
  236.  
  237. If ropen() fails, the wc program goes to the trouble to check errno for
  238. ENOENT rather than just assuming that the failure was caused by a missing
  239. file.
  240.  
  241. The wc program also sets the field and text delimiters even though it is
  242. unneccessary here since they are the same as the default values.  If you
  243. wanted to read a comma-delimited file, you could set the the delimiters to
  244.  
  245.     rsetfldch(rp, ',');
  246.     rsettxtch(rp, '"');
  247.  
  248. which allows you to also read text fields containing commas by delimiting
  249. the text with quotes, such as "Hello, World."
  250.  
  251. Fields are counted using the rnumfld function, which counts all the fields 
  252. in the current record.  In reading a data file, you could use rnumfld() 
  253. to count the number of fields each time a record is read.  This would give 
  254. you a quick check that the expected number of fields was found prior to 
  255. processing the record.
  256.  
  257. The recio library gives you more control over your input data than stdio.
  258. If the last field is missing from a data file, fscanf() starts reading the
  259. next line.  In a file with a complex structure, it can be difficult to tell
  260. where you are when something goes awry.  Sometimes every record in a file 
  261. has a different format.  The recio library has functions you can use to 
  262. always find out where you are.  You only get the next record when you use 
  263. the rgetrec function.
  264.  
  265. The character count does not include any line termination characters.  The 
  266. recio library strips these out of the record buffer.
  267.  
  268.  
  269. 2.3 Field Functions
  270.  
  271. For most programs you will want to use the recio functions that read or write 
  272. data.  Functions are available to read and write integer, unsigned integer, 
  273. long, unsigned long, float, double, time (time_t and struct tm), character, 
  274. and string data types.  There are two types of field functions: those that 
  275. deal with character delimited fields (such as comma-delimited) and those that 
  276. deal with column delimited fields (such as an integer between columns 1 and 5).  
  277. Each type is further divided in two: one for numeric data in base 10 and the 
  278. other for numeric data in any base from 2 to 36.
  279.  
  280.     Class    Description
  281.    ------    ----------------------------------------------------------------
  282.        r     character delimited fields; next field; numeric data in base 10
  283.       rc     column delimited fields;   from column; numeric data in base 10
  284.       rn     character delimited fields; from field; numeric data in base 10
  285.       rb     character delimited fields; next field; numeric data in any base
  286.      rcb     column delimited fields;   from column; numeric data in any base
  287.      rnb     character delimited fields; from field; numeric data in any base
  288.  
  289. A mnemonic system makes it easy to construct the name of any function you 
  290. want.  There are six prefixes (one for each class), two bodies (get reads 
  291. data; put writes data), ten suffixes (one for each data type).  The rb, rcb,
  292. and rnb prefixes are used only with the i, l, ui, and ul suffixes.  also the 
  293. rn and rnb prefixes are used only with the get body.
  294.  
  295.     Prefix   Body   Suffix      Prefix   Body   Suffix
  296.     ------   ----   ------      ------   ----   ------
  297.       r       get      c          rb      get      i
  298.       rc      put      d          rcb     put      l
  299.       rn               f          rnb             ui
  300.                        i                          ul
  301.                        l
  302.                        s
  303.                        t
  304.                       tm
  305.                       ui
  306.                       ul
  307.  
  308. Example:  The rbgetui() function takes record pointer and base arguments,
  309.           and returns an unsigned integer.
  310.  
  311. Additional information on these functions is found in the text file SPEC.TXT.
  312.  
  313.  
  314. 2.4 Error Handling
  315.  
  316. Rather than checking errno and rerror() for errors after each call to a recio
  317. function, or checking the return value from those functions that return an
  318. error value, you can register a callback error function using the rseterrfn
  319. function.  The error function gives you one convenient place to handle all
  320. recio errors.  As you write your error function, you will find that the recio
  321. library provides many useful functions for determining and reporting the
  322. location and type of error.
  323.  
  324. The dif program reads through two files line by line looking for the first 
  325. difference between the two files.  It uses a very simple callback error 
  326. function that just reports the error and then exits the program.
  327.  
  328. /* dif.c - locate line where two text files first differ */
  329.  
  330. #include <errno.h>
  331. #include <stdio.h>
  332. #include <stdlib.h>
  333. #include <string.h>
  334.  
  335. #include "recio.h"
  336.  
  337. /* simple callback error function */
  338. void rerrfn(REC *rp) 
  339. {
  340.     if (risvalid(rp)) {
  341.         fprintf(stderr, "FATAL ERROR: %s - %s\n", 
  342.          rnames(rp), rerrstr(rp));
  343.     } else {
  344.         fprintf(stderr, "FATAL ERROR: %s\n", strerror(errno));
  345.     }
  346.     exit(2);
  347. }
  348.  
  349. /* file open failure error function */
  350. void fopenerr(char *filename)
  351. {
  352.     fprintf(stderr, "FATAL ERROR: %s - %s\n", 
  353.      filename, strerror(errno));
  354.     exit(2);
  355. }
  356.  
  357. int main(int argc, char *argv[])
  358. {
  359.     int errorlevel=1; /* return errorlevel (1=files different; 0=same) */
  360.     REC *rp1;         /* record pointer for first file */
  361.     REC *rp2;         /* record pointer for second file */
  362.  
  363.     if (argc != 3) {
  364.         fprintf(stderr, "Usage: dif file1 file2\n");
  365.         exit(2);
  366.     }
  367.  
  368.     /* register callback error function */
  369.     rseterrfn(rerrfn);
  370.  
  371.     /* open first record stream */
  372.     rp1 = ropen(argv[1], "r");
  373.     if (!rp1 && errno == ENOENT) fopenerr(argv[1]);
  374.  
  375.     /* open second record stream */
  376.     rp2 = ropen(argv[2], "r");
  377.     if (!rp2 && errno == ENOENT) fopenerr(argv[2]);
  378.  
  379.     /* read files line by line */
  380.     for (;;) {
  381.     
  382.         rgetrec(rp1);
  383.         rgetrec(rp2);
  384.         
  385.         /* if neither file has reached the end */
  386.         if (!reof(rp1) && !reof(rp2)) {
  387.             if (strcmp(rrecs(rp1), rrecs(rp2))) {
  388.                 printf("Files first differ at line %ld\n\n", rrecno(rp1));
  389.                 printf("%s:\n%s\n\n", rnames(rp1), rrecs(rp1));
  390.                 printf("%s:\n%s\n\n", rnames(rp2), rrecs(rp2));
  391.                 break;
  392.             }
  393.  
  394.         /* if file 1 ended first */
  395.         } else if (reof(rp1) && !reof(rp2)) {
  396.             printf("File %s ends before file %s\n\n",
  397.              rnames(rp1), rnames(rp2));
  398.             break;
  399.  
  400.         /* if file 2 ended first */
  401.         } else if (!reof(rp1) && reof(rp2)) {
  402.             printf("File %s ends before file %s\n\n",
  403.              rnames(rp2), rnames(rp1));
  404.             break;
  405.  
  406.         /* else both files have reached the end simultaneously */
  407.         } else {
  408.             printf("Files %s and %s are identical\n", 
  409.              rnames(rp1), rnames(rp2));
  410.             errorlevel = 0;
  411.             break;
  412.         }
  413.     }
  414.     rcloseall();
  415.     return errorlevel;
  416. }
  417.  
  418. One important kind of error is the data error.  Data errors occur when data
  419. values are too large or too small, when fields contain illegal characters, 
  420. or when data is missing.  Your error function can correct data errors either
  421. through algorithms (such as the rfix functions used in the test programs) 
  422. or by asking the user for a replacement value.
  423.  
  424. For an example of a callback error function that handles data errors, see 
  425. the TESTCHG.C source code.  A skeleton code structure for a callback error 
  426. function is given in the file DESIGN.TXT.
  427.  
  428.  
  429. 2.5 Warnings
  430.  
  431. Warnings are less serious than errors and for some programs you may well 
  432. decide that they do not need to be considered.  The primary differences 
  433. between errors and warnings come into play if you decide to ignore them 
  434. by not registering callback error and warning functions.  On error, the 
  435. recio library stores the error number and stops reading or writing the 
  436. record stream.  On warning, the recio library continues to read or write 
  437. the record stream, and only stores the warning number until another 
  438. warning comes along to replace it.  
  439.  
  440. If you check the error number just before closing a record stream, you get 
  441. the number of the first error encountered.  On the other hand, if you check 
  442. the warning number at this point, you get the last warning.  Warnings are 
  443. handled with a set of routines analogous to the error handling routines.
  444.  
  445. What kinds of warnings can you get?  One warning lets you know if you have 
  446. read in an empty data string.  Another warning occurs if you write to a 
  447. columnar field and the width between the columns is too small to hold the 
  448. data.  You will find some examples of callback warning functions in the 
  449. source code for the test programs.  A skeleton code structure for a callback 
  450. warning function is given in the file DESIGN.TXT.
  451.  
  452. Simple callback error and warning functions rerrmsg and rwarnmsg are now 
  453. included in the RECIO library.  In the initial prototyping stages of 
  454. development, you may wish to use these functions rather than taking the 
  455. time to develop your own functions.  Later you can substitute more robust 
  456. callback functions.
  457.  
  458.  
  459. 3.0 WHAT NOW?
  460.  
  461. That's it for this brief introduction.  Next, if you haven't already done
  462. so, spend a few minutes running the test programs and perusing the test 
  463. source code.  Then to study the recio functions in more detail, move on to 
  464. the remaining documentation.
  465.  
  466.  
  467. 4.0 REFERENCES
  468.  
  469. Kernighan, B.W. and Ritchie, D.M.  The C Programming Language, Second
  470. Edition.  Prentice Hall, Englewood Cliffs, NJ, 1988.
  471.  
  472. Plauger, P.J.  The Standard C Library.  Prentice Hall, Englewood Cliffs, 
  473. NJ, 1992.
  474.